package repository

import (
	"context"
	"errors"

	"github.com/Tencent/WeKnora/internal/types"
	"github.com/Tencent/WeKnora/internal/types/interfaces"
	"gorm.io/gorm"
)

// modelRepository implements the model repository interface
type modelRepository struct {
	db *gorm.DB
}

// NewModelRepository creates a new model repository
func NewModelRepository(db *gorm.DB) interfaces.ModelRepository {
	return &modelRepository{db: db}
}

// Create creates a new model
func (r *modelRepository) Create(ctx context.Context, m *types.Model) error {
	return r.db.WithContext(ctx).Create(m).Error
}

// GetByID retrieves a model by ID
func (r *modelRepository) GetByID(ctx context.Context, tenantID uint, id string) (*types.Model, error) {
	var m types.Model
	if err := r.db.WithContext(ctx).Where("id = ?", id).Where(
		"tenant_id = ? or tenant_id = 0", tenantID,
	).First(&m).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil
		}
		return nil, err
	}
	return &m, nil
}

// List lists models with optional filtering
func (r *modelRepository) List(
	ctx context.Context, tenantID uint, modelType types.ModelType, source types.ModelSource,
) ([]*types.Model, error) {
	var models []*types.Model
	query := r.db.WithContext(ctx).Where(
		"tenant_id = ? or tenant_id = 0", tenantID,
	)

	if modelType != "" {
		query = query.Where("type = ?", modelType)
	}

	if source != "" {
		query = query.Where("source = ?", source)
	}

	if err := query.Find(&models).Error; err != nil {
		return nil, err
	}

	return models, nil
}

// Update updates a model
func (r *modelRepository) Update(ctx context.Context, m *types.Model) error {
	return r.db.WithContext(ctx).Debug().Model(&types.Model{}).Where(
		"id = ? AND tenant_id = ?", m.ID, m.TenantID,
	).Updates(m).Error
}

// Delete deletes a model
func (r *modelRepository) Delete(ctx context.Context, tenantID uint, id string) error {
	// Find the model first to check if it exists
	model := &types.Model{}
	result := r.db.WithContext(ctx).Where("id = ?", id).Where(
		"tenant_id = ? or tenant_id = 0", tenantID,
	).First(model)
	
	if result.Error != nil {
		if errors.Is(result.Error, gorm.ErrRecordNotFound) {
			return errors.New("no model found to delete")
		}
		return result.Error
	}
	
	// Check if the model is referenced by any knowledge bases or other entities
	// This prevents deletion of models that are currently in use
	if r.isModelReferenced(ctx, id, tenantID) {
		return errors.New("model is currently referenced by one or more knowledge bases and cannot be deleted")
	}
	
	// Perform soft delete by updating the deleted_at field
	result = r.db.WithContext(ctx).Where("id = ?", id).Where(
		"tenant_id = ? or tenant_id = 0", tenantID,
	).Delete(&types.Model{})
	
	if result.Error != nil {
		return result.Error
	}
	
	if result.RowsAffected == 0 {
		return errors.New("no model found to delete")
	}
	
	return nil
}

// isModelReferenced checks if a model is referenced by any knowledge bases or other entities
func (r *modelRepository) isModelReferenced(ctx context.Context, modelID string, tenantID uint) bool {
	// Check if any knowledge base is using this model
	// Check embedding_model_id, summary_model_id, rerank_model_id, vlm_model_id in knowledge_bases table
	var count int64
	
	// Check knowledge_bases table for references
	err := r.db.WithContext(ctx).Model(&types.KnowledgeBase{}).
		Where("tenant_id = ? AND (embedding_model_id = ? OR summary_model_id = ? OR rerank_model_id = ? OR vlm_model_id = ?)",
			tenantID, modelID, modelID, modelID, modelID).
		Count(&count).Error
	
	if err != nil {
	// If there's an error, we'll conservatively assume there's a reference to prevent deletion
	return true
	}
	
	if count > 0 {
		return true
	}
	
	// Check knowledge table for embedding_model_id references
	err = r.db.WithContext(ctx).Model(&types.Knowledge{}).
		Where("embedding_model_id = ?", modelID).
		Count(&count).Error
	
	if err != nil {
		return true
	}
	
	if count > 0 {
		return true
	}
	
	// Check session table for references (rerank_model_id and summary_model_id)
	err = r.db.WithContext(ctx).Model(&types.Session{}).
		Where("tenant_id = ? AND (rerank_model_id = ? OR summary_model_id = ?)",
			tenantID, modelID, modelID).
		Count(&count).Error
	
	if err != nil {
		return true
	}
	
	if count > 0 {
		return true
	}
	
	// Check chat_manage table for references (chat_model_id and rerank_model_id)
	// Note: This assumes there's a chat management table that might reference models
	// If this table doesn't exist, this check might need to be adjusted
	
	// For now, we'll just check if there are any references in the common model reference patterns
	// The checks above should cover the main references based on the types found in the codebase
	
	return false
}
